package org.jeecg.modules.model.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang.StringUtils;
import org.jeecg.common.exception.ParamCheckException;
import org.jeecg.modules.core.constant.YesOrNotEnum;
import org.jeecg.modules.enums.NodeTypeEnum;
import org.jeecg.modules.maps.model.param.BuildFloorQuery;
import org.jeecg.modules.model.entity.BimModelUnit;
import org.jeecg.modules.model.mapper.BimModelUnitMapper;
import org.jeecg.modules.model.model.params.BimModelUnitBatchAddParam;
import org.jeecg.modules.model.model.params.BimModelUnitParam;
import org.jeecg.modules.model.model.params.BimModelUnitTagParam;
import org.jeecg.modules.model.model.result.BimModelUnitBatchAddResult;
import org.jeecg.modules.model.service.IBimModelUnitService;
import org.jeecg.modules.project.entity.BimProjectFloor;
import org.jeecg.modules.project.entity.BuildResultType;
import org.jeecg.modules.project.model.result.BimProjectBuildInfo;
import org.jeecg.modules.project.model.result.BimProjectFloorResult;
import org.jeecg.modules.project.service.IBimProjectFloorService;
import org.jeecg.modules.project.service.IProjectInfoService;
import org.jeecg.modules.seq.entity.SeqConst;
import org.jeecg.modules.seq.service.impl.SequenceService;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Description: 支节点/构件
 * @Author: jeecg-boot
 * @Date: 2023-04-18
 * @Version: V1.0
 */
@Service
public class BimModelUnitServiceImpl extends ServiceImpl<BimModelUnitMapper, BimModelUnit> implements IBimModelUnitService {

    @Autowired
    private IBimProjectFloorService floorService;

    @Autowired
    private SequenceService sequenceService;

    @Autowired
    private IProjectInfoService projectInfoService;

    @Override
    @Transactional
    public BimModelUnitBatchAddResult batchAdd(BimModelUnitBatchAddParam batchAddParam) {
        Integer floorId = batchAddParam.getFloorId();
        Long unitId = batchAddParam.getUnitId();
        if (ObjectUtil.isNotNull(floorId)) {
            return addFloorNode(batchAddParam);
        } else if (ObjectUtil.isNotNull(unitId)) {
            return addParentUnitNode(batchAddParam);
        }
        throw new ParamCheckException("批量添加数据格式不合法!");
    }

    private BimModelUnitBatchAddResult addFloorNode(BimModelUnitBatchAddParam batchAddParam) {
        BimModelUnitBatchAddResult batchAddResult = new BimModelUnitBatchAddResult();
        Integer floorId = batchAddParam.getFloorId();
        BimProjectFloor projectFloor = floorService.getById(floorId);
        if (ObjectUtil.isNull(projectFloor)) {
            throw new ParamCheckException("floorId:" + floorId + "楼层未找到,添加失败!");
        } else {
            batchAddResult.setFloorId(floorId);
            batchAddResult.setNodeType(batchAddParam.getNodeType());
        }
        if (CollUtil.isNotEmpty(batchAddParam.getChildrens())) {
            batchAddParam.getChildrens().stream().forEach(item -> {
                BimModelUnit childModelUnit = new BimModelUnit();
                Integer nodeType = item.getNodeType();
                if (NodeTypeEnum.STAND.getValue().equals(nodeType)) {
                    childModelUnit.setName(item.getName());
                    childModelUnit.setNodeType(nodeType);
                    childModelUnit.setBuildId(projectFloor.getBuildId());
                    childModelUnit.setFloorId(projectFloor.getFloorId());
                    //childModelUnit.setUnitId(sequenceService.nextVal(SeqConst.SEQ_MODEL_UNIT).intValue());
                    this.save(childModelUnit);
                    this.addChildUnitNode(childModelUnit, item.getChildrens());
                } else if (NodeTypeEnum.UNIT.getValue().equals(nodeType)) {
                    this.checkBimUnit(item);
                    childModelUnit.setName(item.getName());
                    childModelUnit.setNodeType(nodeType);
                    childModelUnit.setBuildId(projectFloor.getBuildId());
                    childModelUnit.setFloorId(projectFloor.getFloorId());
                    childModelUnit.setSourceBimId(item.getSourceBimId());
                    childModelUnit.setSourceDir(item.getSourceDir());
                    //childModelUnit.setUnitId(sequenceService.nextVal(SeqConst.SEQ_MODEL_UNIT).intValue());
                    this.save(childModelUnit);
                } else {
                    log.error("不支持的节点类型" + JSONUtil.toJsonStr(item));
                }
                if (ObjectUtil.isNotNull(childModelUnit)) {
                    batchAddResult.getChildrens().add(childModelUnit.convertResult());
                }
            });
        }
        return batchAddResult;
    }

    /**
     * @Description: 添加支节点
     * @Param: batchAddParam
     * @Date: 2023-04-19 10:07
     */
    private BimModelUnitBatchAddResult addParentUnitNode(BimModelUnitBatchAddParam batchAddParam) {
        BimModelUnitBatchAddResult batchAddResult = new BimModelUnitBatchAddResult();
        Long unitId = batchAddParam.getUnitId();
        BimModelUnit modelUnit = this.getById(unitId);
        if (ObjectUtil.isNull(modelUnit)) {
            throw new ParamCheckException("unitId:" + unitId + "支节点未找到,添加失败!");
        } else {
            batchAddResult.setUnitId(unitId);
            batchAddResult.setNodeType(modelUnit.getNodeType());
        }
        if (NodeTypeEnum.UNIT.getValue().equals(modelUnit.getNodeType())) {
            throw new ParamCheckException("unitId:" + unitId + "非支节点,添加失败!");
        }
        List<BimModelUnitParam> childAddParam = batchAddParam.getChildrens();
        this.addChildUnitNode(modelUnit, childAddParam);
        batchAddResult.setChildrens(modelUnit.convertResult().getChildrens());
        return batchAddResult;
    }

    /**
     * @Description: 添加支节点子节点
     * @Param: [modelUnit, childAddParam]
     * @Date: 2023-04-19 10:07
     */
    private void addChildUnitNode(BimModelUnit modelUnit, List<BimModelUnitParam> childAddParam) {
        if (CollUtil.isNotEmpty(childAddParam)) {
            childAddParam.stream().forEach(item -> {
                Integer nodeType = item.getNodeType();
                if (NodeTypeEnum.STAND.getValue().equals(nodeType)) {
                    BimModelUnit standModelUnit = addStandNode(modelUnit, item, nodeType);
                    this.addChildUnitNode(standModelUnit, item.getChildrens());
                    modelUnit.convertResult().getChildrens().add(standModelUnit.convertResult());
                } else if (NodeTypeEnum.UNIT.getValue().equals(nodeType)) {
                    this.checkBimUnit(item);
                    BimModelUnit unitModelUnit = addUnitNode(modelUnit, item, nodeType);
                    modelUnit.convertResult().getChildrens().add(unitModelUnit.convertResult());
                } else {
                    log.error("不支持的节点类型" + JSONUtil.toJsonStr(item));
                }
            });
        }
    }

    private BimModelUnit addStandNode(BimModelUnit modelUnit, BimModelUnitParam item, Integer nodeType) {
        BimModelUnit childModelUnit = new BimModelUnit();
        childModelUnit.setName(item.getName());
        childModelUnit.setNodeType(nodeType);
        childModelUnit.setBuildId(modelUnit.getBuildId());
        childModelUnit.setFloorId(modelUnit.getFloorId());
        childModelUnit.setParentId(modelUnit.getUnitId());
        childModelUnit.setParentIds(getParentIds(modelUnit));
        //childModelUnit.setUnitId(sequenceService.nextVal(SeqConst.SEQ_MODEL_UNIT).intValue());
        this.save(childModelUnit);
        return childModelUnit;
    }

    @NotNull
    private String getParentIds(BimModelUnit modelUnit) {
        StringBuilder builder = new StringBuilder();
        if (!StringUtils.isBlank(modelUnit.getParentIds())) {
            builder.append(modelUnit.getParentIds());
        }
        builder.append(modelUnit.getUnitId().toString() + ",");
        return builder.toString();
    }

    private BimModelUnit addUnitNode(BimModelUnit modelUnit, BimModelUnitParam item, Integer nodeType) {
        BimModelUnit childModelUnit = new BimModelUnit();
        childModelUnit.setName(item.getName());
        childModelUnit.setNodeType(nodeType);
        childModelUnit.setBuildId(modelUnit.getBuildId());
        childModelUnit.setFloorId(modelUnit.getFloorId());
        //构件节点可以设置标签
        childModelUnit.setTagCode(modelUnit.getTagCode());
        childModelUnit.setSourceBimId(item.getSourceBimId());
        childModelUnit.setSourceDir(item.getSourceDir());
        childModelUnit.setParentId(modelUnit.getUnitId());
        childModelUnit.setParentIds(getParentIds(modelUnit));
        //childModelUnit.setUnitId(sequenceService.nextVal(SeqConst.SEQ_MODEL_UNIT).intValue());
        this.save(childModelUnit);
        return childModelUnit;
    }

    @Override
    public void deleteByIds(List<String> idsList) {
        if (CollUtil.isNotEmpty(idsList)) {
            idsList.stream().forEach(item -> {
                this.deleteById(item);
            });
        }
    }

    @Override
    @Transactional
    public void deleteById(String item) {
        this.removeById(item);
        List<BimModelUnit> childModelUnits = this.getBaseMapper().findAllChild(item);
        if (CollUtil.isNotEmpty(childModelUnits)) {
            List<Long> idsList = childModelUnits.stream().map(node -> node.getUnitId()).collect(Collectors.toList());
            this.removeByIds(idsList);
        }
    }

    @Override
    public List<JSONObject> getProjBuildInfos(BuildFloorQuery query, BuildResultType resultType) {
        List<JSONObject> buildList = new ArrayList<>();
        List<BimProjectBuildInfo> projectBuildInfoList = projectInfoService.getProjBuildInfos(query, resultType);
        if (CollUtil.isNotEmpty(projectBuildInfoList)) {
            projectBuildInfoList.stream().forEach(item -> {
                JSONObject buildObj = new JSONObject();
                buildObj.set("buildId", item.getBuildId());
                buildObj.set("buildNo", item.getBuildNo());
                buildObj.set("nodeType", 1);
                if (CollUtil.isNotEmpty(item.getFloors())) {
                    List<JSONObject> floorList = new ArrayList<>();
                    for (BimProjectFloorResult floorResult : item.getFloors()) {
                        JSONObject floorObj = new JSONObject();
                        floorObj.set("floorId", floorResult.getFloorId());
                        floorObj.set("floorNo", floorResult.getFloorNo());
                        floorObj.set("nodeType", 1);
                        List<BimModelUnit> unitList = this.getUnit(floorResult.getBuildId(), floorResult.getFloorId());
                        if (CollUtil.isNotEmpty(unitList)) {
                            List<JSONObject> floorUnitList = new ArrayList<>();
                            unitList.stream().forEach(modelUnit -> {
                                if (ObjectUtil.isNull(modelUnit.getParentId())) {
                                    JSONObject modelUnitObj = new JSONObject();
                                    if (NodeTypeEnum.STAND.getValue().equals(modelUnit.getNodeType())) {
                                        modelUnitObj.set("unitId", String.valueOf(modelUnit.getUnitId()));
                                        modelUnitObj.set("name", modelUnit.getName());
                                        modelUnitObj.set("nodeType", modelUnit.getNodeType());
                                        addChildJson(modelUnitObj, modelUnit, unitList);
                                    } else if (NodeTypeEnum.UNIT.getValue().equals(modelUnit.getNodeType())) {
                                        modelUnitObj.set("unitId", String.valueOf(modelUnit.getUnitId()));
                                        modelUnitObj.set("name", modelUnit.getName());
                                        modelUnitObj.set("sourceBimId", modelUnit.getSourceBimId());
                                        modelUnitObj.set("sourceDir", modelUnit.getSourceDir());
                                        modelUnitObj.set("tagCode", modelUnit.getTagCode());
                                        modelUnitObj.set("nodeType", modelUnit.getNodeType());
                                    }
                                    floorUnitList.add(modelUnitObj);
                                }
                            });
                            floorObj.set("childrens", floorUnitList);
                        }
                        floorList.add(floorObj);
                    }
                    buildObj.set("childrens", floorList);
                }
                buildList.add(buildObj);
            });
        }
        return buildList;
    }

    @Override
    public List<JSONObject> queryUnits(BimModelUnitParam modelUnitParam) {
        if (isQueryBuildFloor(modelUnitParam)) {
            BuildFloorQuery query = new BuildFloorQuery();
            query.setBuildId(modelUnitParam.getBuildId());
            query.setFloorId(modelUnitParam.getFloorId());
            return getProjBuildInfos(query, BuildResultType.UNIT);
        } else {
            List<JSONObject> unitJsonList = new ArrayList<>();
            List<BimModelUnit> modelUnitList = this.getUnit(modelUnitParam);
            if (CollUtil.isNotEmpty(modelUnitList)) {
                modelUnitList.stream().forEach(modelUnit -> {
                    JSONObject modelUnitObj = new JSONObject();
                    if (NodeTypeEnum.STAND.getValue().equals(modelUnit.getNodeType())) {
                        modelUnitObj.set("unitId", String.valueOf(modelUnit.getUnitId()));
                        modelUnitObj.set("name", modelUnit.getName());
                        modelUnitObj.set("nodeType", modelUnit.getNodeType());
                        addChildJson(modelUnitObj, modelUnit, this.getBaseMapper().findAllChild(String.valueOf(modelUnit.getUnitId())));
                    } else if (NodeTypeEnum.UNIT.getValue().equals(modelUnit.getNodeType())) {
                        modelUnitObj.set("unitId", String.valueOf(modelUnit.getUnitId()));
                        modelUnitObj.set("name", modelUnit.getName());
                        modelUnitObj.set("sourceBimId", modelUnit.getSourceBimId());
                        modelUnitObj.set("sourceDir", modelUnit.getSourceDir());
                        modelUnitObj.set("tagCode", modelUnit.getTagCode());
                        modelUnitObj.set("nodeType", modelUnit.getNodeType());
                    }
                    unitJsonList.add(modelUnitObj);
                });
            }
            return unitJsonList;
        }
    }

    @Override
    public void tagNode(BimModelUnitTagParam unitTagParam) {
        if(ObjectUtil.isNull(unitTagParam.getUnitId())){
            throw new ParamCheckException("主键唯一值不能为空!");
        }
        BimModelUnit bimModelUnit = this.getById(unitTagParam.getUnitId());
        if(ObjectUtil.isNull(bimModelUnit)){
            throw new ParamCheckException("unitId："+unitTagParam.getUnitId()+"构件未找到!");
        }
        if(!NodeTypeEnum.UNIT.getValue().equals(bimModelUnit.getNodeType())){
            throw new ParamCheckException("该节点非构件,设置构件标签失败!");
        }
        bimModelUnit.setTagCode(unitTagParam.getTagCode());
        this.updateById(bimModelUnit);
    }

    @Override
    public void add(BimModelUnit bimModelUnit) {
        if(ObjectUtil.isNotNull(bimModelUnit.getParentId())){
            BimModelUnit parentModelUnit = this.getById(bimModelUnit.getParentId());
            bimModelUnit.setParentIds(this.getParentIds(parentModelUnit));
            if(ObjectUtil.isNull(bimModelUnit.getFloorId())){
                bimModelUnit.setFloorId(bimModelUnit.getFloorId());
            }
            if(ObjectUtil.isNull(bimModelUnit.getBuildId())){
                bimModelUnit.setBuildId(bimModelUnit.getBuildId());
            }
        }
        bimModelUnit.setDelFlag(YesOrNotEnum.N.getCode());
        this.save(bimModelUnit);
    }

    private boolean isQueryBuildFloor(BimModelUnitParam modelUnitParam) {
        boolean isBuildQuery = true;
        if (StringUtils.isNotBlank(modelUnitParam.getName())) {
            isBuildQuery = false;
        }
        if (StringUtils.isNotBlank(modelUnitParam.getSourceBimId())) {
            isBuildQuery = false;
        }
        if (StringUtils.isNotBlank(modelUnitParam.getSourceDir())) {
            isBuildQuery = false;
        }
        if (StringUtils.isNotBlank(modelUnitParam.getTagCode())) {
            isBuildQuery = false;
        }
        if (ObjectUtil.isNotNull(modelUnitParam.getNodeType())
                && NodeTypeEnum.UNIT.getValue().equals(modelUnitParam.getNodeType())) {
            isBuildQuery = false;
        }
        if (ObjectUtil.isNotNull(modelUnitParam.getUnitId())) {
            isBuildQuery = false;
        }
        return isBuildQuery;
    }

    private void addChildJson(JSONObject modelUnitObj, BimModelUnit modelUnit, List<BimModelUnit> unitList) {
        List<JSONObject> standUnitList = new ArrayList<>();
        for (BimModelUnit iterModelUnit : unitList) {
            if (ObjectUtil.isNotNull(iterModelUnit.getParentId()) && iterModelUnit.getParentId().equals(modelUnit.getUnitId())) {
                JSONObject childUnitObj = new JSONObject();
                if (NodeTypeEnum.STAND.getValue().equals(iterModelUnit.getNodeType())) {
                    childUnitObj.set("unitId", String.valueOf(iterModelUnit.getUnitId()));
                    childUnitObj.set("parentId", String.valueOf(iterModelUnit.getParentId()));
                    childUnitObj.set("name", iterModelUnit.getName());
                    childUnitObj.set("nodeType", iterModelUnit.getNodeType());
                    addChildJson(childUnitObj, iterModelUnit, unitList);
                } else if (NodeTypeEnum.UNIT.getValue().equals(iterModelUnit.getNodeType())) {
                    childUnitObj.set("unitId", String.valueOf(iterModelUnit.getUnitId()));
                    childUnitObj.set("parentId", String.valueOf(iterModelUnit.getParentId()));
                    childUnitObj.set("name", iterModelUnit.getName());
                    childUnitObj.set("sourceBimId", iterModelUnit.getSourceBimId());
                    childUnitObj.set("sourceDir", iterModelUnit.getSourceDir());
                    childUnitObj.set("tagCode", iterModelUnit.getTagCode());
                    childUnitObj.set("nodeType", iterModelUnit.getNodeType());
                }
                standUnitList.add(childUnitObj);
            }
        }
        modelUnitObj.set("childrens", standUnitList);
    }

    public List<BimModelUnit> getUnit(Integer buildId, Integer floorId) {
        return new LambdaQueryChainWrapper<BimModelUnit>(this.getBaseMapper())
                .eq(BimModelUnit::getBuildId, buildId)
                .eq(BimModelUnit::getFloorId, floorId)
                .eq(BimModelUnit::getDelFlag, YesOrNotEnum.N.getCode())
                .list();
    }

    public List<BimModelUnit> getUnit(BimModelUnitParam modelUnitParam) {
        return new LambdaQueryChainWrapper<BimModelUnit>(this.getBaseMapper())
                .eq(ObjectUtil.isNotNull(modelUnitParam.getBuildId()), BimModelUnit::getBuildId, modelUnitParam.getBuildId())
                .eq(ObjectUtil.isNotNull(modelUnitParam.getFloorId()), BimModelUnit::getFloorId, modelUnitParam.getFloorId())
                .eq(StringUtils.isNotBlank(modelUnitParam.getName()), BimModelUnit::getName, modelUnitParam.getName())
                .eq(StringUtils.isNotBlank(modelUnitParam.getSourceBimId()), BimModelUnit::getSourceBimId, modelUnitParam.getSourceBimId())
                .eq(StringUtils.isNotBlank(modelUnitParam.getSourceDir()), BimModelUnit::getSourceDir, modelUnitParam.getSourceDir())
                .eq(StringUtils.isNotBlank(modelUnitParam.getTagCode()), BimModelUnit::getTagCode, modelUnitParam.getTagCode())
                .eq(ObjectUtil.isNotNull(modelUnitParam.getNodeType()), BimModelUnit::getNodeType, modelUnitParam.getNodeType())
                .eq(ObjectUtil.isNotNull(modelUnitParam.getUnitId()), BimModelUnit::getUnitId, modelUnitParam.getUnitId())
                .list();
    }

    /**
     * @Description: 根据sourceBimId进行唯一性校验
     * @Param: [modelUnitParam]
     * @Date: 2023-04-19 10:11
     */
    public void checkBimUnit(BimModelUnitParam modelUnitParam) {
        if (StringUtils.isBlank(modelUnitParam.getSourceBimId())) {
            return;
        }
        LambdaQueryWrapper<BimModelUnit> modelUnitQuery = new LambdaQueryWrapper<>();
        modelUnitQuery.eq(BimModelUnit::getSourceBimId, modelUnitParam.getSourceBimId())
                .eq(BimModelUnit::getDelFlag, YesOrNotEnum.N.getCode());
        List<BimModelUnit> modelUnitList = this.list(modelUnitQuery);
        if (CollUtil.isNotEmpty(modelUnitList)) {
            StringBuilder builder = new StringBuilder();
            builder.append(modelUnitParam.getName());
            String unitName = modelUnitParam.getNodeType() == 1 ? "支节点" : "构件";
            builder.append(unitName);
            builder.append("已经存在!");
            throw new ParamCheckException(builder.toString());
        }
    }

}
